package com.google.code.hibernate.rest;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.code.hibernate.rest.impl.EntityManagerImpl;
import com.google.code.hibernate.rest.impl.ExceptionHandlerImpl;
import com.google.code.hibernate.rest.method.DeleteMethodProcessor;
import com.google.code.hibernate.rest.method.GetMethodProcessor;
import com.google.code.hibernate.rest.method.OptionsMethodProcessor;
import com.google.code.hibernate.rest.method.PostMethodProcessor;
import com.google.code.hibernate.rest.method.PutMethodProcessor;

/**
 * 
 * @author wangzijian
 * 
 */
public abstract class RestInterface extends HttpServlet {

	private static final long serialVersionUID = 1L;
	public static final String ENCODING = "UTF-8";
	
	protected final Logger logger = LoggerFactory.getLogger(getClass());

	protected ExceptionHandler exceptionHandler;
	protected EntityManager entityManager;
	
	protected GetMethodProcessor get;
	protected DeleteMethodProcessor delete;
	protected PostMethodProcessor post;
	protected PutMethodProcessor put;
	protected OptionsMethodProcessor options;

	protected abstract SessionFactory getSessionFactory();

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		logRequestIfNecessary(request);
		try {
			super.service(request, response);
		} catch (RuntimeException e) {
			logger.error("Service error", e);
			if (exceptionHandler.canHandle(e.getClass())) {
				exceptionHandler.handle(e, request, response);
			} else {
				throw e;
			}
		} finally {
			CurrentSession.close();
		}
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
		post.process(request, response);
	}

	@Override
	protected void doPut(HttpServletRequest request, HttpServletResponse response) throws IOException {
		put.process(request, response);
	}

	@Override
	protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException {
		delete.process(request, response);
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
		get.process(request, response);
	}

	@Override
	protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws IOException {
		options.process(request, response);
	}

	@Override
	public void init() throws ServletException {
		super.init();
		entityManager = createEntityManager();
		get = new GetMethodProcessor(entityManager);
		delete = new DeleteMethodProcessor(entityManager);
		post = new PostMethodProcessor(entityManager);
		put = new PutMethodProcessor(entityManager);
		options = new OptionsMethodProcessor(entityManager);
		
		exceptionHandler = createExceptionHandler();
	}

	protected EntityManager createEntityManager() {
		return new EntityManagerImpl(getSessionFactory());
	}

	protected ExceptionHandler createExceptionHandler() {
		return new ExceptionHandlerImpl(options);
	}

	private void logRequestIfNecessary(HttpServletRequest request) {
		if (!logger.isDebugEnabled()) {
			return;
		}
		logger.debug(request.getMethod() + " " + request.getRequestURL());
		Enumeration<?> headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			Object header = headerNames.nextElement();
			logger.debug("Header " + header + "=" + request.getHeader(header.toString()));
		}
	}
}
